#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/uaccess.h>

#define DRVCNT 1                  // 设备个数
#define DRVNAME "devtree_led_drv" // 设备名称

#define LED_ON 1
#define LED_OFF 0

static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

struct device_tree_drv
{
    dev_t devid;              // 设备号
    int major;                // 主设备号
    int minor;                // 次设备号
    struct cdev cdevid;       // 字符设备
    struct class *class;      // 设备类
    struct device *device;    // 设备
    struct device_node *node; // 设备节点
};

struct device_tree_drv devtree_led;

static void led_control(unsigned int cmd)
{
    u32 val = 0;
    if (cmd == LED_ON)
    {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }
    else if (cmd == LED_OFF)
    {
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
}

ssize_t devtree_led_write(struct file *filp, const char __user *buff, size_t size, loff_t *pos)
{
    unsigned int cmd = 0;
    int ret;
    ret = copy_from_user(&cmd, buff, size);
    if (ret > 0)
    {
        printk("copy from user error\r\n");
        return -EINVAL;
    }
    // 根据数据点灯
    led_control(cmd);
    return 0;
}

int devtree_led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &devtree_led;
    printk("devtree_led_open\r\n");

    return 0;
}

int devtree_led_close(struct inode *inode, struct file *filp)
{
    struct device_tree_drv *data = filp->private_data;
    printk("devtree_led_close\r\n");
    printk("major = %d minor = %d\r\n", data->major, data->minor);
    return 0;
}

// 字符设备操作集
struct file_operations devtree_fops = {
    .owner = THIS_MODULE,
    .write = devtree_led_write,
    .open = devtree_led_open,
    .release = devtree_led_close,
};

static int __init device_tree_drv_init(void)
{
    int ret;
    const char *str;
    u32 regdata[10];
    int i = 0;
    u32 val;

    // 注册设备号
    // 如果存在主设备号
    if (devtree_led.major)
    {
        devtree_led.devid = MKDEV(devtree_led.major, 0);
        ret = register_chrdev_region(devtree_led.devid, DRVCNT, DRVNAME);
    }
    else // 如果不存在主设备号
    {
        ret = alloc_chrdev_region(&devtree_led.devid, 0, DRVCNT, DRVNAME);
        devtree_led.major = MAJOR(devtree_led.devid);
        devtree_led.minor = MINOR(devtree_led.devid);
    }
    if (ret < 0)
    {
        printk("register devid error\r\n");
        goto register_devid_err;
    }

    // 关联fops
    cdev_init(&devtree_led.cdevid, &devtree_fops);
    // 添加到内核
    ret = cdev_add(&devtree_led.cdevid, devtree_led.devid, DRVCNT);
    if (ret)
    {
        printk("cdev add error\r\n");
        goto cdev_add_err;
    }

    // 创建设备类
    devtree_led.class = class_create(THIS_MODULE, DRVNAME);
    if (IS_ERR(devtree_led.class))
    {
        ret = PTR_ERR(devtree_led.class);
        goto class_create_err;
    }

    // 创建设备节点
    devtree_led.device = device_create(devtree_led.class, NULL, devtree_led.devid, NULL, DRVNAME);
    if (IS_ERR(devtree_led.device))
    {
        ret = PTR_ERR(devtree_led.device);
        goto device_create_err;
    }

    // 获取设备树中的设备节点devtree_led
    devtree_led.node = of_find_node_by_path("/devtree_led");
    if (!devtree_led.node)
    {
        printk("find_node error\r\n");
        ret = -EINVAL;
        goto find_node_err;
    }

    // 解析status
    ret = of_property_read_string(devtree_led.node, "status", &str);
    if (ret)
    {
        printk("read_string error\r\n");
        ret = -EINVAL;
        goto find_node_err;
    }
    else
    {
        printk("status=%s\r\n", str);
    }

    // 解析reg的内容
    ret = of_property_read_u32_array(devtree_led.node, "reg", regdata, 10);
    if (ret)
    {
        printk("read_u32_array error\r\n");
        ret = -EINVAL;
        goto find_node_err;
    }
    else
    {
        for (i = 0; i < 5; i++)
        {
            printk("%#x %#x\r\n", regdata[i * 2], regdata[i * 2 + 1]);
        }
    }

    // 映射为虚拟地址
    CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
    SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
    SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
    GPIO1_DR = ioremap(regdata[6], regdata[7]);
    GPIO1_GDIR = ioremap(regdata[8], regdata[9]);

    // 初始化LED
    val = readl(CCM_CCGR1);
    val &= ~(3 << 26);
    val |= 3 << 26;
    writel(val, CCM_CCGR1);

    // 设置IO复用
    writel(0x5, SW_MUX_GPIO1_IO03);
    // 设置电气属性
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    // 设置输出
    val = readl(GPIO1_GDIR);
    val |= 1 << 3;
    writel(val, GPIO1_GDIR);

    // 输出高电平关闭灯
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    return 0;
find_node_err:
    device_destroy(devtree_led.class, devtree_led.devid);
device_create_err:
    class_destroy(devtree_led.class);
class_create_err:
    cdev_del(&devtree_led.cdevid);
cdev_add_err:
    unregister_chrdev_region(devtree_led.devid, DRVCNT);
register_devid_err:
    return ret;
}

static void __exit device_tree_drv_exit(void)
{
    u32 val = 0;
    // 输出高电平关闭灯
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    iounmap(CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    // 注销设备
    device_destroy(devtree_led.class, devtree_led.devid);
    // 摧毁类
    class_destroy(devtree_led.class);
    // 取消cdev关联
    cdev_del(&devtree_led.cdevid);
    // 注销设备号
    unregister_chrdev_region(devtree_led.devid, DRVCNT);
}

module_init(device_tree_drv_init);
module_exit(device_tree_drv_exit);
MODULE_LICENSE("GPL");